import { drawSelectionCursor } from '../display/selection.js';
import { operation } from '../display/operations.js';
import { clipPos } from '../line/pos.js';
import { posFromMouse } from '../measurement/position_measurement.js';
import { eventInWidget } from '../measurement/widgets.js';
import { makeChange, replaceRange } from '../model/changes.js';
import { changeEnd } from '../model/change_measurement.js';
import { simpleSelection } from '../model/selection.js';
import { setSelectionNoUndo, setSelectionReplaceHistory } from '../model/selection_updates.js';
import { ie, presto, safari } from '../util/browser.js';
import { elt, removeChildrenAndAdd } from '../util/dom.js';
import { e_preventDefault, e_stop, signalDOMEvent } from '../util/event.js';
import { indexOf } from '../util/misc.js';

// Kludge to work around strange IE behavior where it'll sometimes
// re-fire a series of drag-related events right after the drop (#1551)
let lastDrop = 0;

export function onDrop(e) {
  let cm = this;
  clearDragCursor(cm);
  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
  e_preventDefault(e);
  if (ie) lastDrop = +new Date();
  let pos = posFromMouse(cm, e, true),
    files = e.dataTransfer.files;
  if (!pos || cm.isReadOnly()) return;
  // Might be a file drop, in which case we simply extract the text
  // and insert it.
  if (files && files.length && window.FileReader && window.File) {
    let n = files.length,
      text = Array(n),
      read = 0;
    const markAsReadAndPasteIfAllFilesAreRead = () => {
      if (++read == n) {
        operation(cm, () => {
          pos = clipPos(cm.doc, pos);
          let change = {
            from: pos,
            to: pos,
            text: cm.doc.splitLines(text.filter(t => t != null).join(cm.doc.lineSeparator())),
            origin: 'paste',
          };
          makeChange(cm.doc, change);
          setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))));
        })();
      }
    };
    const readTextFromFile = (file, i) => {
      if (cm.options.allowDropFileTypes && indexOf(cm.options.allowDropFileTypes, file.type) == -1) {
        markAsReadAndPasteIfAllFilesAreRead();
        return;
      }
      let reader = new FileReader();
      reader.onerror = () => markAsReadAndPasteIfAllFilesAreRead();
      reader.onload = () => {
        let content = reader.result;
        if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) {
          markAsReadAndPasteIfAllFilesAreRead();
          return;
        }
        text[i] = content;
        markAsReadAndPasteIfAllFilesAreRead();
      };
      reader.readAsText(file);
    };
    for (let i = 0; i < files.length; i++) readTextFromFile(files[i], i);
  } else {
    // Normal drop
    // Don't do a replace if the drop happened inside of the selected text.
    if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
      cm.state.draggingText(e);
      // Ensure the editor is re-focused
      setTimeout(() => cm.display.input.focus(), 20);
      return;
    }
    try {
      let text = e.dataTransfer.getData('Text');
      if (text) {
        let selected;
        if (cm.state.draggingText && !cm.state.draggingText.copy) selected = cm.listSelections();
        setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
        if (selected)
          for (let i = 0; i < selected.length; ++i)
            replaceRange(cm.doc, '', selected[i].anchor, selected[i].head, 'drag');
        cm.replaceSelection(text, 'around', 'paste');
        cm.display.input.focus();
      }
    } catch (e) {}
  }
}

export function onDragStart(cm, e) {
  if (ie && (!cm.state.draggingText || +new Date() - lastDrop < 100)) {
    e_stop(e);
    return;
  }
  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;

  e.dataTransfer.setData('Text', cm.getSelection());
  e.dataTransfer.effectAllowed = 'copyMove';

  // Use dummy image instead of default browsers image.
  // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
  if (e.dataTransfer.setDragImage && !safari) {
    let img = elt('img', null, null, 'position: fixed; left: 0; top: 0;');
    img.src = '';
    if (presto) {
      img.width = img.height = 1;
      cm.display.wrapper.appendChild(img);
      // Force a relayout, or Opera won't use our image for some obscure reason
      img._top = img.offsetTop;
    }
    e.dataTransfer.setDragImage(img, 0, 0);
    if (presto) img.parentNode.removeChild(img);
  }
}

export function onDragOver(cm, e) {
  let pos = posFromMouse(cm, e);
  if (!pos) return;
  let frag = document.createDocumentFragment();
  drawSelectionCursor(cm, pos, frag);
  if (!cm.display.dragCursor) {
    cm.display.dragCursor = elt('div', null, 'CodeMirror-cursors CodeMirror-dragcursors');
    cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
  }
  removeChildrenAndAdd(cm.display.dragCursor, frag);
}

export function clearDragCursor(cm) {
  if (cm.display.dragCursor) {
    cm.display.lineSpace.removeChild(cm.display.dragCursor);
    cm.display.dragCursor = null;
  }
}
